{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit FileHistory;

interface

uses Classes, Menus, SysUtils;

const
  DEF_MAX_ITEMS = 5;

type
  THistoryRefreshEvent = TNotifyEvent;
  THistorySelectionEvent = procedure (Sender: TObject; FileName: TFileName) of object;

  TAHMFileHistoryItem = class(TCollectionItem)
  private
    FFileName: TFileName;
  protected
  public
    function IsMatch(FileName: TFileName): Boolean;
    procedure Assign(Source: TPersistent); override;
  published
    property FileName: TFileName read FFileName write FFileName;
  end;

  TAHMFileHistoryList = class(TCollection)
  private
    FOnRefresh: THistoryRefreshEvent;
    FOnSelect: THistorySelectionEvent;
    FMenuItem: TMenuItem;
    FMaximum: Integer;
    FCurrentFile: TAHMFileHistoryItem;
    procedure SetOnRefresh(OnRefresh: THistoryRefreshEvent);
    procedure SetRootMenuItem(MenuItem: TMenuItem);
    function GetItem(Index: Integer): TAHMFileHistoryItem;
    procedure SetItem(Index: Integer; Value: TAHMFileHistoryItem);
  protected
    function FindFile(FileName: TFileName): TAHMFileHistoryItem;
    function PopFile(FileName: TFileName): Boolean;
    procedure PushFile(FileName: TFileName);
    procedure DoRefresh;
    procedure BuildMenuItems;
    procedure MenuSelect(Sender: TObject);
  public
    constructor Create;
    property OnRefresh: THistoryRefreshEvent read FOnRefresh write SetOnRefresh;
    property OnSelect: THistorySelectionEvent read FOnSelect write FOnSelect;
    property CurrentFile: TAHMFileHistoryItem read FCurrentFile;
    property Items[Index: Integer]: TAHMFileHistoryItem read GetItem write SetItem; default;
    property RootMenuItem: TMenuItem read FMenuItem write SetRootMenuItem;
    property MaximumItems: Integer read FMaximum write FMaximum;
    procedure TouchFile(FileName: TFileName);
    procedure RemoveFile(FileName: TFileName);
  end;

implementation

// TAHMFileHistoryItem

function TAHMFileHistoryItem.IsMatch(FileName: TFileName): Boolean;
var
  File1, File2: TFileName;
begin
  // Expand any short filenames and ignore case differences and whitespace
  File1 := Trim(UpperCase(ExpandFileName(FFilename)));
  File2 := Trim(UpperCase(ExpandFileName(FileName)));
  Result := (File1 = File2);
end;

procedure TAHMFileHistoryItem.Assign(Source: TPersistent);
begin
  with Source as TAHMFileHistoryItem do
  begin
    Self.FileName := FileName;
  end;
end;

// TAHMFileHistoryList

constructor TAHMFileHistoryList.Create;
begin
  // We'll own a collection of file history items
  inherited Create(TAHMFileHistoryItem);

  // Set default properties
  FMaximum := DEF_MAX_ITEMS;
end;

procedure TAHMFileHistoryList.SetOnRefresh(OnRefresh: TNotifyEvent);
begin
  FOnRefresh := OnRefresh;
  DoRefresh;
end;

procedure TAHMFileHistoryList.SetRootMenuItem(MenuItem: TMenuItem);
begin
  FMenuItem := MenuItem;
  DoRefresh;
end;

function TAHMFileHistoryList.GetItem(Index: Integer): TAHMFileHistoryItem;
begin
  Result := TAHMFileHistoryItem(inherited GetItem(Index));
end;

procedure TAHMFileHistoryList.SetItem(Index: Integer; Value: TAHMFileHistoryItem);
begin
  inherited SetItem(Index, Value);
end;

function TAHMFileHistoryList.FindFile(FileName: TFileName): TAHMFileHistoryItem;
var
  i: Integer;
begin
  // Set default result - no match found
  Result := nil;

  for i := 0 to Pred(Count) do
    if Items[i].IsMatch(FileName) then
    begin
      Result := Items[i];
      Break;
    end;
end;

function TAHMFileHistoryList.PopFile(FileName: TFileName): Boolean;
var
  MatchItem: TAHMFileHistoryItem;
begin
  // Set default result - not found
  Result := False;

  MatchItem := FindFile(FileName);
  if Assigned(MatchItem) then
  begin
    Delete(MatchItem.Index);
    Result := True;
  end;

  // Clear current file cache
  FCurrentFile := nil;
end;

procedure TAHMFileHistoryList.PushFile(FileName: TFileName);
var
  NewItem: TAHMFileHistoryItem;
begin
  NewItem := TAHMFileHistoryItem(Insert(0));
  NewItem.FileName := FileName;

  // Cache our new file so gui may exclude it from displayed list if required
  FCurrentFile := NewItem;
end;

procedure TAHMFileHistoryList.TouchFile(FileName: TFileName);
begin
  // Remove any previous occurences of this file
  while PopFile(FileName) do;

  // Trim list size
  while (Count >= FMaximum) and (FMaximum > 0) do Delete(Pred(Count));

  // Push new file onto stack
  PushFile(FileName);

  // And refresh any attached UI controls
  DoRefresh;
end;

procedure TAHMFileHistoryList.RemoveFile(FileName: TFileName);
begin
  // Remove any previous occurences of this file
  while PopFile(FileName) do;

  // And refresh any attached UI controls
  DoRefresh;
end;

procedure TAHMFileHistoryList.DoRefresh;
begin
  if Assigned(FOnRefresh) then FOnRefresh(Self);
  if Assigned(FMenuItem) then BuildMenuItems;
end;

procedure TAHMFileHistoryList.BuildMenuItems;
var
  i, Num: Integer;
  SubItem: TMenuItem;
begin
  // Initialise counter for menu items
  Num := 0;
  FMenuItem.Clear;

  // Update current menu item contents
  for i := 0 to Pred(Count) do
    if Items[i] <> FCurrentFile then
    begin
      SubItem := TMenuItem.Create(FMenuItem);
      SubItem.Caption := IntToStr(Num) + ' ' + Items[i].FileName;
      SubItem.OnClick := MenuSelect;
      SubItem.Tag := i;
      FMenuItem.Add(SubItem);
      Inc(Num);
    end;

  // Only enable if we have some recent files
  FMenuItem.Enabled := FMenuItem.Count > 0;
end;

procedure TAHMFileHistoryList.MenuSelect(Sender: TObject);
var
  FileName: TFileName;
begin
  // Convert tag of selection to associated filename
  FileName := Items[TMenuItem(Sender).Tag].FileName;

  // Remove file from list if it doesn't exist
  if not FileExists(FileName) then
  begin
    while PopFile(FileName) do;
    DoRefresh;
  end;

  if Assigned(FOnSelect) then FOnSelect(Self, FileName);
end;


end.
